import * as lsp from "vscode-languageserver/node";
import {
    existsSync,
    readdirSync,
    statSync
} from "fs";
import {
    dirname,
    isAbsolute
} from "path";
import {
    basicTypes,
    CharacterCodes,
    getGlobalLib,
    getGlobalScope,
    t
} from "./analysis";
import {
    LspIntrinsicType,
    LspObjectType,
    LspType
} from "./types";
import {
    getCompletionItemKindFromSymbol,
    getFinalSymbol,
    getNodeWithPathAtPosition,
    getTopLevelSourceFile,
    inCondition,
    inIterator,
    makeDetailFromSymbol,
    makeDocumentFromSymbol,
    NodeAtPostionResult
} from "./util";

const operators = [
    "And",
    "Or",
    "Xor",
    "Like",
    "Mod",
    "Not",
    "In",
    "Is",
];

// completion
const keywords = [
    "Case",
    "Const",
    "Dim",
    "Do",
    "Each",
    "Else",
    "ElseIf",
    "End",
    "Error",
    "Exit",
    "Explicit",
    "False",
    "For",
    "Function",
    "GlobalVariables",
    "GoTo",
    "If",
    "Implicit",
    "Loop",
    "Next",
    "Null",
    "On",
    "Option",
    "Paper",
    "Resume",
    "Section",
    "Select",
    "Set",
    "Step",
    "Sub",
    "Then",
    "To",
    "True",
    "Until",
    "While",
    "With",
];

// declare keywords
const declareKeywords: string[] = [
    "Module",
    "Interface",
    "Class",
    "Inherits",
    "Implements",
    "Property",
    "Function",
    "Sub",
    "Const",
    "ReadOnly",
    "WriteOnly",
    "As",
    "Default",
    "Optional",
];

// metadata keywords
const metadataKeywords: string[] = [
    "style",
    "labelstyle",
    "templates",
    "long",
    "double",
    "text",
    "date",
    "boolean",
    "categorical",
    "define",
    "info",
    "compound",
    "block",
    "page",
    "db",
    "codes",
    "expression",
    "deriveelements",
    "noderiveelements",
    "validation",
    "initialanswer",
    "defaultanswer",
    "nocasedata",
    "unversioned",
    "axis",
    "usagetype",
    "helperfields",
    "precision",
    "rotate",
    "rot",
    "randomize",
    "ran",
    "reverse",
    "rev",
    "ascending",
    "asc",
    "descending",
    "desc",
    "namespace",
    "use",
    "sublist",
    "exclusive",
    "factor",
    "keycode",
    "fix",
    "canfilter",
    "nofilter",
    "other",
    "multiplier",
    "elementtype",
    "dk",
    "ref",
    "na",
    "fields",
    "row",
    "column",
    "expand",
    "noexpand",
    "grid",
    "inline",
    "lcl",
    "scale",
];

function createCompletionItem(label: string, kind: lsp.CompletionItemKind = lsp.CompletionItemKind.Keyword, insertText?: string): lsp.CompletionItem {
    return {
        label,
        kind,
        insertText: insertText || label,
    };
}

function createCompletionList(entries: lsp.CompletionItem[] = [], isIncomplete?: boolean): lsp.CompletionList {
    return lsp.CompletionList.create(entries, isIncomplete);
}

// operators completions
const operatorCompletionEntries = operators.map<lsp.CompletionItem>(item => createCompletionItem(item, lsp.CompletionItemKind.Operator));
// keywords completions
const keywordCompletionEntries = keywords.map<lsp.CompletionItem>(item => createCompletionItem(item)).concat(operatorCompletionEntries);
// declare keywords completions
const declareKeywordCompletionEntries = declareKeywords.map<lsp.CompletionItem>(item => createCompletionItem(item));
const declareKeywordCompletionList = createCompletionList(declareKeywordCompletionEntries, true);
// metadata keywords completions
const metadataKeywordCompletionEntries = metadataKeywords.map<lsp.CompletionItem>(item => createCompletionItem(item));
const metadataKeywordCompletionList = createCompletionList(metadataKeywordCompletionEntries, true);
// section keywords completions
const sectionKeywordComletions = {
    job: createCompletionList(t.sectionMemberNames.job.map<lsp.CompletionItem>(item => createCompletionItem(item, lsp.CompletionItemKind.Property)), false),
    inputDataSource: createCompletionList(t.sectionMemberNames.inputDataSource.map<lsp.CompletionItem>(item => createCompletionItem(item, lsp.CompletionItemKind.Property)), false),
    outputDataSource: createCompletionList(t.sectionMemberNames.outputDataSource.map<lsp.CompletionItem>(item => createCompletionItem(item, lsp.CompletionItemKind.Property)), false),
    globalSQLVariables: createCompletionList(t.sectionMemberNames.globalSQLVariables.map<lsp.CompletionItem>(item => createCompletionItem(item, lsp.CompletionItemKind.Property)), false),
    logging: createCompletionList(t.sectionMemberNames.logging.map<lsp.CompletionItem>(item => createCompletionItem(item, lsp.CompletionItemKind.Property)), false),
};

const preprocessorKeywords = [ "include", "define", "if", "ifdef", "ifndef", "undef", "elif", "else", "endif", "error", "line" ];
// preprocessor keyword
const preProKeywordCompletionEntries = preprocessorKeywords.map<lsp.CompletionItem>(item => createCompletionItem(item));
const preProKeywordCompletionList = createCompletionList(preProKeywordCompletionEntries, false);

function setIntrinsicTypeCompletions(type: t.IntrinsicType, ...completions: lsp.CompletionItem[]) {
    ((type as LspIntrinsicType).completions || ((type as LspIntrinsicType).completions = createCompletionList())).items.push(...completions);
}

function setTypeCompletions(type: t.Type, ...completions: lsp.CompletionItem[]) {
    ((type as LspType).completions || ((type as LspType).completions = createCompletionList())).items.push(...completions);
}

function setObjectTypeCompletions(type: t.ObjectType, ...completion: lsp.CompletionItem[]) {
    ((type as LspObjectType).completions || ((type as LspObjectType).completions = createCompletionList())).items.push(...completion);
}

function setLocalTypeCompletions(type: t.LocalType, ...completions: lsp.CompletionItem[]) {
    if (type.isObjectType()) {
        setObjectTypeCompletions(type, ...completions);
    }
    else if (type.isType()) {
        setTypeCompletions(type, ...completions);
    }
    else {
        setIntrinsicTypeCompletions(type, ...completions);
    }
}

function makeCompletionItemFromSymbol(symbol: t.Symbol, node?: t.Node): lsp.CompletionItem {
    return {
        label: symbol.name,
        kind: getCompletionItemKindFromSymbol(symbol),
        detail: makeDetailFromSymbol(symbol, node),
        documentation: {
            kind: "markdown",
            value: makeDocumentFromSymbol(symbol, node, false),
        }
    };
}

function updateObjectSymbolMemberCompletions(localType: t.LocalType, symbol: t.Symbol) {
    if (symbol.is(t.SymbolKind.module)) {
        symbol.members.forEach(member => {
            updateObjectSymbolMemberCompletions(member.type, member);
        });
    }
    else if (symbol.is(t.SymbolKind.interface | t.SymbolKind.class | t.SymbolKind.enum)) {
        symbol.members.forEach(member => {
            setLocalTypeCompletions(localType, makeCompletionItemFromSymbol(member, member.declaration.name));
        });
    }
}

// built-in completions
const builtInCompletions: lsp.CompletionItem[] = [];
const builtInBasicTypeCompletions: { [key: number]: lsp.CompletionItem[] } = {
    [t.TypeFlag.any]: [],
    [t.TypeFlag.string]: [],
    [t.TypeFlag.date]: [],
    [t.TypeFlag.long]: [],
    [t.TypeFlag.double]: [],
    [t.TypeFlag.boolean]: [],
    [t.TypeFlag.categorical]: [],
    [t.TypeFlag.object]: [],
    [t.TypeFlag.arrayType]: [],
};
// initialize global built-in declaration completion items.
getGlobalScope().forEach(v => {
    if (v.is(t.SymbolKind.function | t.SymbolKind.enum | t.SymbolKind.constant | t.SymbolKind.macro)) {
        let item = makeCompletionItemFromSymbol(v, v.declaration.name);
        builtInCompletions.push(item);
        if (v.is(t.SymbolKind.function)) {
            if (v.parameters.length === 0) {
                return;
            }
            let firstParamTypeFlag = v.parameters[0].tracker.latest().flag;
            if (firstParamTypeFlag & t.TypeFlag.arrayType) {
                builtInBasicTypeCompletions[t.TypeFlag.arrayType].push(item);
            }
            // any 的补全信息添加到除了Object的所有类型的列表中
            else if (firstParamTypeFlag & t.TypeFlag.any) {
                builtInBasicTypeCompletions[t.TypeFlag.arrayType].push(item);
                builtInBasicTypeCompletions[t.TypeFlag.any].push(item);
                setIntrinsicTypeCompletions(basicTypes.any, item);
                builtInBasicTypeCompletions[t.TypeFlag.string].push(item);
                setIntrinsicTypeCompletions(basicTypes.string, item);
                builtInBasicTypeCompletions[t.TypeFlag.date].push(item);
                setIntrinsicTypeCompletions(basicTypes.date, item);
                builtInBasicTypeCompletions[t.TypeFlag.long].push(item);
                setIntrinsicTypeCompletions(basicTypes.long, item);
                builtInBasicTypeCompletions[t.TypeFlag.double].push(item);
                setIntrinsicTypeCompletions(basicTypes.double, item);
                builtInBasicTypeCompletions[t.TypeFlag.boolean].push(item);
                setIntrinsicTypeCompletions(basicTypes.boolean, item);
                builtInBasicTypeCompletions[t.TypeFlag.categorical].push(item);
                setIntrinsicTypeCompletions(basicTypes.categorical, item);
            }
            else if (firstParamTypeFlag & t.TypeFlag.string) {
                builtInBasicTypeCompletions[t.TypeFlag.string].push(item);
                setIntrinsicTypeCompletions(basicTypes.string, item);
            }
            else if (firstParamTypeFlag & t.TypeFlag.date) {
                builtInBasicTypeCompletions[t.TypeFlag.date].push(item);
                setIntrinsicTypeCompletions(basicTypes.date, item);
            }
            else if (firstParamTypeFlag & t.TypeFlag.long) {
                builtInBasicTypeCompletions[t.TypeFlag.long].push(item);
                setIntrinsicTypeCompletions(basicTypes.long, item);
            }
            else if (firstParamTypeFlag & t.TypeFlag.double) {
                builtInBasicTypeCompletions[t.TypeFlag.double].push(item);
                setIntrinsicTypeCompletions(basicTypes.double, item);
            }
            else if (firstParamTypeFlag & t.TypeFlag.boolean) {
                builtInBasicTypeCompletions[t.TypeFlag.boolean].push(item);
                setIntrinsicTypeCompletions(basicTypes.boolean, item);
            }
            else if (firstParamTypeFlag & t.TypeFlag.categorical) {
                builtInBasicTypeCompletions[t.TypeFlag.categorical].push(item);
                setIntrinsicTypeCompletions(basicTypes.categorical, item);
            }
            else if (firstParamTypeFlag & t.TypeFlag.object) {
                builtInBasicTypeCompletions[t.TypeFlag.object].push(item);
                setIntrinsicTypeCompletions(basicTypes.object, item);
            }
        }
    }
    if (v.is(t.SymbolKind.class) &&
        v.declaration.isSignature<t.ClassSignature>(t.SyntaxKind.classSignature) &&
        v.declaration.constants.length > 0) {
        builtInCompletions.push(makeCompletionItemFromSymbol(v, v.declaration.name));
    }
    // 创建全局定义的补全信息
    if (v.is(t.SymbolKind.class | t.SymbolKind.interface | t.SymbolKind.module | t.SymbolKind.enum)) {
        const type = v.type;
        updateObjectSymbolMemberCompletions(type, v);
    }
}, t.SymbolScope.declare);

export const emptyCompletionList = createCompletionList([], true);
// default completion items for script file (.mrs/.dms)
const defaultCompletionItems = keywordCompletionEntries.concat(builtInCompletions);
// default completion list for script file
export const defaultCompletionList = createCompletionList(defaultCompletionItems, true /** allow auto add local text */);
// basic type completion lists
const builtInBasicTypeCompletionLists = {
    [t.TypeFlag.any]: createCompletionList(builtInBasicTypeCompletions[t.TypeFlag.any]),
    [t.TypeFlag.string]: createCompletionList(builtInBasicTypeCompletions[t.TypeFlag.string]),
    [t.TypeFlag.date]: createCompletionList(builtInBasicTypeCompletions[t.TypeFlag.date]),
    [t.TypeFlag.long]: createCompletionList(builtInBasicTypeCompletions[t.TypeFlag.long]),
    [t.TypeFlag.double]: createCompletionList(builtInBasicTypeCompletions[t.TypeFlag.double]),
    [t.TypeFlag.boolean]: createCompletionList(builtInBasicTypeCompletions[t.TypeFlag.boolean]),
    [t.TypeFlag.categorical]: createCompletionList(builtInBasicTypeCompletions[t.TypeFlag.categorical]),
    [t.TypeFlag.object]: createCompletionList(builtInBasicTypeCompletions[t.TypeFlag.object]),
    [t.TypeFlag.arrayType]: createCompletionList(builtInBasicTypeCompletions[t.TypeFlag.arrayType]),
};

function makeCompletionItemsFromSymbolTable(table: t.ReadonlySymbolTable | ReadonlyMap<string, t.Symbol>) {
    let items: lsp.CompletionItem[] = [];
    table.forEach(member => {
        if (member.is(t.SymbolKind.constant | t.SymbolKind.enum) || // 常量和枚举定义提供提示
            (member.is(t.SymbolKind.class) && member.declaration.isSignature<t.ClassSignature>(t.SyntaxKind.classSignature) && member.declaration.constants.length > 0) // 如果Class定义中包含常量，也提供提示
        ) {
            items.push(makeCompletionItemFromSymbol(member));
        }
    });
    return items;
}

// using lib constants
const usingLibCompletions = {
    tom: makeCompletionItemsFromSymbolTable(getGlobalLib("tom").members),
    dom: makeCompletionItemsFromSymbolTable(getGlobalLib("dom").members),
    mdm: makeCompletionItemsFromSymbolTable(getGlobalLib("mdm").members),
    iom: makeCompletionItemsFromSymbolTable(getGlobalLib("iom").members),
    qom: makeCompletionItemsFromSymbolTable(getGlobalLib("qom").members),
    core: makeCompletionItemsFromSymbolTable(getGlobalLib("core").members),
};

function getSymbolMemberCompletions(symbol: t.Symbol): lsp.CompletionItem[] {
    return ((symbol.type as LspIntrinsicType).completions.items || []).concat(builtInBasicTypeCompletions[t.TypeFlag.object]);
}

/**
 * 获取指定文件夹路径（需要是绝对路径）内的所有文件和文件夹名，并将其创建为补全列表并返回。
 * @param uri 已知文件路径，需要是绝对路径
 * @returns 指定文件路径下的文件夹和文件组成的补全列表
 */
function getPathCompletions(uri: string): lsp.CompletionItem[] {
    let items: lsp.CompletionItem[] = [];
    if (!isAbsolute(uri)) {
        return items;
    }
    if (!existsSync(uri)) {
        return items;
    }
    let stat = statSync(uri);
    if (stat.isFile()) {
        uri = dirname(uri);
    }
    const fileNames = readdirSync(uri, { withFileTypes: true });
    for (const f of fileNames) {
        if (f.isDirectory()) {
            items.push({
                label: f.name,
                kind: lsp.CompletionItemKind.Folder
            });
        }
        else if (f.isFile()) {
            items.push({
                label: f.name,
                kind: lsp.CompletionItemKind.File
            });
        }
    }
    return items;
}

function getMetadataElementsIndicesCompletions(symbol: t.Symbol, brace?: { open: string, close: string }): lsp.CompletionItem[] {
    const result: lsp.CompletionItem[] = [];
    if (symbol.isMDM(t.SymbolMDMKind.field)) {
        symbol.locals.forEach(cat => {
            if (cat.declaration.is<t.MDMCategory>(t.SyntaxKind.mdmCategory) &&
                cat.declaration.list) {
                // use list in categories
                let listSymbol = cat.declaration.list.symbol;
                if (listSymbol) {
                    result.push(...getMetadataElementsIndicesCompletions(listSymbol));
                }
            }
            else {
                result.push({
                    label: cat.name,
                    kind: lsp.CompletionItemKind.Constant,
                    detail: cat.name,
                    documentation: {
                        kind: "markdown",
                        value: "```dmrs\n" + cat.name + "\n```"
                    },
                    insertText: `${brace ? brace.open : ""}${cat.name}${brace ? brace.close : ""}`
                });
            }
        });
    }
    return result;
}

function getLineLocations(result: NodeAtPostionResult, source: t.SourceFile): t.Symbol[] {
    let path = result.path;
    let locationBlock: t.LocationBlockNode;
    // functions
    if (path.functionDeclaration) {
        locationBlock = path.functionDeclaration.body;
    }
    // scopes
    else if (path.eventSection) {
        locationBlock = path.eventSection.statement;
    }
    else if (path.outsideEventSection) {
        locationBlock = path.outsideEventSection.statement;
    }
    else if (path.routingSection) {
        locationBlock = path.routingSection.statement;
    }
    // file
    else {
        locationBlock = source;
    }

    return Array.from(locationBlock.locations.symbols.values());
}

function getScriptFileCompletion(source: t.SourceFile, nodeResult: NodeAtPostionResult, position: number, triggerChar: number) {
    // '\' 和 '\' 只在#include中触发
    if ((triggerChar === CharacterCodes.slash || triggerChar === CharacterCodes.backslash) && !nodeResult.path.preProIncludeNode) {
        return emptyCompletionList;
    }
    // dim ...
    if (nodeResult.path.localDeclarations) {
        return emptyCompletionList;
    }
    // '"' or '\' or '/'
    if (triggerChar === CharacterCodes.quotation) {
        // metadata like: field[""].slice ...
        // 只有索引符号'[]'内的内容是字符串时，才触发提醒，如果是表达式，不提醒
        if (nodeResult.node && nodeResult.path.indexExpression &&
            nodeResult.path.indexExpression.index.isStringLiteral() &&
            nodeResult.node.isStringLiteral()) {
            let objSymbol = nodeResult.path.indexExpression.object.symbol;
            if (objSymbol && objSymbol.isMDM(t.SymbolMDMKind.field)) {
                return createCompletionList(getMetadataElementsIndicesCompletions(objSymbol));
            }
        }
        return emptyCompletionList;
    }
    // 其余情况下，如果在字符串中，不提示
    if (nodeResult.node?.isStringLiteral()) {
        return emptyCompletionList;
    }
    // trigger: {
    if (triggerChar === CharacterCodes.openCurlyBrace) {
        // like: field[{}].slice ...
        if (nodeResult.path.indexExpression) {
            let objectSymbol = nodeResult.path.indexExpression.object.symbol;
            if (objectSymbol && objectSymbol.isMDM(t.SymbolMDMKind.field)) {
                return createCompletionList(getMetadataElementsIndicesCompletions(objectSymbol));
            }
        }
        return emptyCompletionList;
    }
    // trigger: [
    if (triggerChar === CharacterCodes.openSquareBracket) {
        // like: field[]
        if (nodeResult.path.indexExpression) {
            let objectSymbol = nodeResult.path.indexExpression.object.symbol;
            if (objectSymbol && objectSymbol.isMDM(t.SymbolMDMKind.field)) {
                return createCompletionList(getMetadataElementsIndicesCompletions(objectSymbol, { open: "{", close: "}" }));
            }
        }
        return emptyCompletionList;
    }
    // 如果在 job / inputdatasource / outputdatasource / log / globalvariables Section中，
    // 在表达式左值，提示相关Section的成员名，在表达式右值，提示本地宏定义。
    let inSpecialSection = nodeResult.path.jobSection || nodeResult.path.inputDataSourceSection || nodeResult.path.outputDataSourceSection || nodeResult.path.loggingSection || nodeResult.path.globalSQLVariablesSection;
    if (nodeResult.path.jobSection && !nodeResult.node) {
        return sectionKeywordComletions.job;
    }
    if (nodeResult.path.inputDataSourceSection && !nodeResult.node) {
        return sectionKeywordComletions.inputDataSource;
    }
    if (nodeResult.path.outputDataSourceSection && !nodeResult.node) {
        return sectionKeywordComletions.outputDataSource;
    }
    if (nodeResult.path.loggingSection && !nodeResult.node) {
        return sectionKeywordComletions.logging;
    }
    if (nodeResult.path.globalSQLVariablesSection && !nodeResult.node) {
        return sectionKeywordComletions.globalSQLVariables;
    }
    if (inSpecialSection) {
        const items: lsp.CompletionItem[] = [];
        for (const s of source.locals.positionAt(position)) {
            if (s.is(t.SymbolKind.macro)) {
                items.push(makeCompletionItemFromSymbol(s));
            }
        }
        return createCompletionList(items, true);
    }
    // member expression
    if (nodeResult.path.memberExpression) {
        let objSymbol = getFinalSymbol(nodeResult.path.memberExpression.object);
        // 如果成员表达式的`object`未定义符号，返回空表
        if (!objSymbol) {
            return emptyCompletionList;
        }
        // `object`符号是函数时，返回空表
        if (objSymbol.is(t.SymbolKind.functionLike)) {
            return emptyCompletionList;
        }
        // `object`符号是枚举类型时，只返回成员补全表
        if (objSymbol.is(t.SymbolKind.enum)) {
            return createCompletionList(getSymbolMemberCompletions(objSymbol));
        }
        if (objSymbol.type.is(t.TypeFlag.basic)) {
            return builtInBasicTypeCompletionLists[objSymbol.type.flag] ?? emptyCompletionList;
        }
        // 需要检查对象符号是否是元数据对象
        let extra: lsp.CompletionItem[] = [];
        if (objSymbol.isMDM(t.SymbolMDMKind.field)) {
            // 需要提醒下级变量定义
            objSymbol.members.forEach(member => {
                extra.push(makeCompletionItemFromSymbol(member));
            });
            // 同时，需要根据对象成员变量的元数据类型，添加对应的基础类型补全对象
            const declaration = objSymbol.declaration;
            if (declaration.isMDMField()) {
                switch (declaration.fieldFlag) {
                    case t.MDMFieldFlags.categorical:
                    case t.MDMFieldFlags.categoricalLoop:
                        extra.push(...builtInBasicTypeCompletions[t.TypeFlag.categorical]);
                        break;
                    case t.MDMFieldFlags.long:
                    case t.MDMFieldFlags.numericLoop:
                        extra.push(...builtInBasicTypeCompletions[t.TypeFlag.long]);
                        break;
                    case t.MDMFieldFlags.double:
                        extra.push(...builtInBasicTypeCompletions[t.TypeFlag.double]);
                        break;
                    case t.MDMFieldFlags.text:
                        extra.push(...builtInBasicTypeCompletions[t.TypeFlag.string]);
                        break;
                    case t.MDMFieldFlags.date:
                        extra.push(...builtInBasicTypeCompletions[t.TypeFlag.date]);
                        break;
                    case t.MDMFieldFlags.boolean:
                        extra.push(...builtInBasicTypeCompletions[t.TypeFlag.boolean]);
                        break;
                }
            }
        }
        return createCompletionList(getSymbolMemberCompletions(objSymbol).concat(extra));
    }
    // 因为可能此文件是通过#include引入到其他文件中的，此时的元数据声明在最上级文件的Scope中，
    // 所以，此处需要寻找最上级的SourceFile对象。
    let topSourceFile = getTopLevelSourceFile(source);
    // 其余类型
    let extra: lsp.CompletionItem[] = [];
    // 根据不同的入口文件类型，提示不同的初始定义
    // - .dms: DOM, MRSCRIPTCORELIB
    // - .mrs: MDM, MRSCRIPTCORELIB
    // - .r.mrs: IOM, MRSCRIPTCORELIB
    switch (topSourceFile.fileKind) {
        case t.FileKind.dms:
            extra.push(...usingLibCompletions.core, ...usingLibCompletions.dom);
            break;
        case t.FileKind.mrs:
            extra.push(...usingLibCompletions.core, ...usingLibCompletions.mdm);
            break;
        case t.FileKind.routing:
            extra.push(...usingLibCompletions.iom, ...usingLibCompletions.core);
            break;
    }

    let scope = source.locals;
    // 宏定义是通用的，首先加入到列表中
    scope.forEach(item => {
        if (item.is(t.SymbolKind.macro)) {
            extra.push(makeCompletionItemFromSymbol(item));
        }
    }, t.SymbolScope.macro);
    // 如果节点路径中包含Event，需要提示Event拥有的Scope中的本地定义
    if (nodeResult.path.eventSection) {
        scope = nodeResult.path.eventSection.locals;
    }
    // 加入本地函数定义
    scope.forEach(func => {
        extra.push(makeCompletionItemFromSymbol(func));
    }, t.SymbolScope.function);
    // 检查是否在OnNextCase中
    let event = nodeResult.path.eventSection || nodeResult.path.outsideEventSection;
    if (event?.isStatement<t.EventSection>(t.SyntaxKind.eventSection) && event.eventSectionFlag === t.EventSectionKind.onNextCase) {
        // 添加元数据提示
        topSourceFile.locals.forEach(meta => {
            extra.push(makeCompletionItemFromSymbol(meta));
        }, t.SymbolScope.metadata);
    }
    // 最后添加本地定义
    if (nodeResult.path.functionDeclaration) {
        scope = nodeResult.path.functionDeclaration.locals;
    }
    for (const item of scope.positionAt(position)) {
        // 跳过宏定义
        if (item.is(t.SymbolKind.macro)) {
            continue;
        }
        extra.push(makeCompletionItemFromSymbol(item));
    }
    // 如果在参数列表中，不提示关键字
    if (nodeResult.path.callExpression || nodeResult.path.indexExpression) {
        return createCompletionList(builtInCompletions.concat(extra));
    }
    // 根据文件类型，提示不同的关键字
    return createCompletionList(defaultCompletionItems.concat(extra));
}

function getMetadataFileCompletion(source: t.SourceFile, nodeResult: NodeAtPostionResult) {
    // 提示本地宏定义
    let macros: lsp.CompletionItem[] = [];
    source.locals.forEach(m => {
        macros.push(makeCompletionItemFromSymbol(m));
    }, t.SymbolScope.macro);
    // 在 use list_name 中，提示已定义的MetadataCategories名称
    if (nodeResult.path.metadataCategory && nodeResult.path.metadataCategory.list) {
        if (source.parent) {
            source = getTopLevelSourceFile(source);
        }
        let items: lsp.CompletionItem[] = [];
        source.locals.forEach(item => {
            if (item.isMDM(t.SymbolMDMKind.categories)) {
                items.push(makeCompletionItemFromSymbol(item));
            }
        }, t.SymbolScope.metadata);
        return createCompletionList(items.concat(macros));
    }
    return createCompletionList(metadataKeywordCompletionEntries.concat(macros));
}

export async function provideCompletion(source: t.SourceFile, position: number, context: lsp.CompletionContext): Promise<lsp.CompletionList> {
    // 在注释中时，不提示
    if (source.getCommentAtPosition(position)) {
        return emptyCompletionList;
    }
    // .d.mrs文件中，只提示关键字
    if (source.fileKind === t.FileKind.declare) {
        return declareKeywordCompletionList;
    }
    // 在文件开头，直接提示关键字
    if (position < 1) {
        return source.fileKind === t.FileKind.mdd ? metadataKeywordCompletionList : defaultCompletionList;
    }
    // 将上一个字符视为触发字符
    let triggerChar: number;
    if (context && context.triggerCharacter) {
        triggerChar = context.triggerCharacter.charCodeAt(0);
    }
    else {
        triggerChar = source.text.charCodeAt(position - 1);
    }
    // get node
    const nodeResult = getNodeWithPathAtPosition(source, position);
    // pre-processor 关键字, triggerChar 必须是 '#', 并且, 需要当前位置不在标识符内, 同时也不在 #define 后或条件语句内
    if (triggerChar === CharacterCodes.numberSign) {
        if (!nodeResult.node?.isIdentifier() && !inCondition(nodeResult.path, position) && !inIterator(nodeResult.path, position)) {
            return preProKeywordCompletionList;
        }
        return emptyCompletionList;
    }
    // #define后，不提示
    if (nodeResult.path.preProDefineNode) {
        return emptyCompletionList;
    }
    // 如果文件对象有父对象，检查父级对象是否有Event，如果有，更新进NodeResult中。
    if (source.parent) {
        let parent = source.getParent<t.EventSection>(n => n.isStatement<t.EventSection>(t.SyntaxKind.eventSection));
        if (parent) {
            nodeResult.path.outsideEventSection = parent;
        }
    }
    // #if
    if (nodeResult.path.preProIfNode && !nodeResult.path.stringLiteral) {
        let localMacros = source.locals.positionAt(position);
        return localMacros.length > 0 ?
            createCompletionList(localMacros.map(e => makeCompletionItemFromSymbol(e))) : emptyCompletionList;
    }
    // #include
    if (triggerChar === CharacterCodes.quotation ||
        triggerChar === CharacterCodes.slash ||
        triggerChar === CharacterCodes.backslash
    ) {
        return nodeResult.path.preProIncludeNode ? createCompletionList(getPathCompletions(nodeResult.path.preProIncludeNode.resolvedPath)) : emptyCompletionList;
    }
    // goto ...
    if (nodeResult.path.jumpStatement && nodeResult.path.jumpStatement.goto) {
        let locs = getLineLocations(nodeResult, source);
        return createCompletionList(locs.map(s => makeCompletionItemFromSymbol(s)), true);
    }
    // metadata
    if (source.fileKind === t.FileKind.mdd || nodeResult.path.metadataSection /** Metadata Section内，应该提示元数据内容 */) {
        return getMetadataFileCompletion(source, nodeResult);
    }
    // script
    if (source.fileKind === t.FileKind.dms || source.fileKind === t.FileKind.mrs || source.fileKind === t.FileKind.routing) {
        return getScriptFileCompletion(source, nodeResult, position, triggerChar);
    }
    return emptyCompletionList;
}

